import logging

from django.conf import settings
from django.shortcuts import render
from django.http import JsonResponse

from oracle import configure

RENDER_METHOD = {
    # 内部渲染
    "internal": 0,
    # 外部渲染
    "external": 1,
}


def judge_render_flag(request, **kwargs):
    """
    判断渲染标志，是内部渲染还是外部渲染
    :param request:
    :param kwargs:
    :return: 通过 RENDER_METHOD 获取渲染标志
    """
    # 是否强制外部渲染标志，程序逻辑传入
    force_external_render = kwargs.get("render")
    # 传入参数没有设置强制标志
    if force_external_render is None:
        # 是否强制后端或前端渲染，为以后扩展，get方法和post方法分开写，内容目前是一样的
        if request.method == "POST":
            force_external_render = request.POST.get("render")
        if not force_external_render:
            force_external_render = request.GET.get("render")
        # else:
        #     force_external_render = None
        # 如果传入标志为数字类型，则转换为int
        if force_external_render is not None and force_external_render.isdigit():
            force_external_render = int(force_external_render)
    if force_external_render is None and hasattr(settings, "RENDER"):
        force_external_render = settings.RENDER
    if isinstance(force_external_render, int) and force_external_render not in RENDER_METHOD.values():
        force_external_render = 0
    else:
        force_external_render = RENDER_METHOD.get(force_external_render, 0)
    return force_external_render


def render_handler(request, status, method=None, template=None, data=None, msg=None, *args, **kwargs):
    """
    渲染函数加壳 为了方便一些
    :param request:
        render: 强制前端渲染标志，True强制前端渲染，False或者不设置则强制后端渲染
    :param status: 状态code代码
    :param method: 传入内部渲染方法
        如果传入method没有操作，data可以包含以下几个内容：
            head_title: 页面标题
            title: 大幕显示标题
            href: 跳转链接，需要时解析后的内容，不能传name。有跳转链接页面在规定秒数中会自动进行跳转。
            button_text: 按钮文本。有跳转链接必须有按钮文本，提示信息，为了能主动点击跳转
        错误信息msg 会使用o_msg或状态内信息内容
    :param template: 传入内部模板
        如果传入，则内部渲染使用传入模板渲染，否则默认使用"oracle/notice.html"
    :param data: 主要数据
    :param msg: 当前状态信息
    :param args:
    :param kwargs:
        render: 如果设置优先级比request参数高，强制前端渲染，True强制前端渲染，False或者不设置则强制后端渲染

        o_version: 外部渲染，版本信息，如没有默认使用settings中b_version数值
    :return:
    """
    # 获取状态信息，包括状态码、内部用状态描述、显示用状态信息
    # 根据str类型key去公有状态表中查找到value
    if isinstance(status, str):
        status_value = settings.STATUS_CODE.get(status)
    # 字典类型说明已经定义好了状态信息要素，直接使用即可
    elif isinstance(status, dict):
        status_value = status
    else:
        status_value = settings.STATUS_CODE.get("server_error")

    if not status_value:
        status_value = settings.STATUS_CODE.get("server_error")

    # 默认数据处理
    o_version = kwargs.get("o_version")
    if not o_version:
        o_version = settings.B_VERSION
    o_status = status_value.get('status')
    o_msg = msg
    if not o_msg:
        o_msg = status_value.get('msg')
    o_data = data
    if not o_data:
        o_data = dict()

    # 组合数据
    response_data = {
        'data': o_data,
        'status': o_status,
        'msg': o_msg,
        'version': o_version,
    }

    force_external_render = judge_render_flag(request, **kwargs)

    # 后端页面渲染
    if force_external_render == 0:
        if template is None:
            template = "oracle/notice.html"
        if method:
            return method(request, template, response_data)
        else:
            return render(request, template, response_data)
    # 设置前端渲染标志，则返回前端json数据
    elif force_external_render == 1:
        return JsonResponse(response_data)
    # 后端页面渲染
    else:
        if template is None:
            template = "oracle/notice.html"
        if method:
            return method(request, template, response_data)
        else:
            return render(request, template, response_data)


def set_dict_data(key, dict_src, dict_des, default=None):
    """
    如果源字典有key 则目标字典进行赋值，如果没有key，则赋值默认value
    :param key: 索引值
    :param dict_src: 源字典
    :param dict_des: 目标字典
    :param default: 默认赋值
    :return:
    """
    if key in dict_src:
        dict_des[key] = dict_src[key]
    else:
        dict_des[key] = default


def get_context_data(request, logger, data, **kwargs):
    """
    为了函数式方便复用，将赋值操作单独进行提取出来
    :param request:
    :param logger: 日志记录
    :param data: 更新context中的data字典
    :param kwargs:
    :return:
    """
    try:
        # 是否强制外部渲染标志
        force_external_render = judge_render_flag(request, **kwargs)

        if force_external_render == 0:
            # 网站标题等内容
            # 网站描述
            set_dict_data('website_description', kwargs, data, configure.DESCRIPTION)
            # 网站作者
            set_dict_data('website_author', kwargs, data, configure.AUTHOR)
            # 网站标题
            set_dict_data('website_title', kwargs, data, configure.WEBSITE_TITLE)
            # 网站欢迎信息
            set_dict_data('website_welcome', kwargs, data, configure.WEBSITE_WELCOME)
            # 状态码
            data['status_code'] = settings.STATUS_CODE
            data['login_url'] = configure.LOGIN_URL_NAME
            data['logout_url'] = configure.LOGOUT_URL_NAME
            data['init_db_url'] = configure.INIT_DB_URL_NAME
            # 权限启用标志
            data['permission_enable'] = settings.PERMISSION_AUTHENTICATE_ENABLE
    except Exception as error:
        logger.error('[BaseMixin]加载基本信息出错')
        logger.error(f'[BaseMixin]加载基本信息出错信息为:[{error}]')


class BaseMixin(object):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.logger = logging.getLogger(__name__)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        data = context.setdefault("data", {})
        # 理论上kwarg里面的data和context中的data相同，但是为了下面的函数调用不冲突，需要删除一个data键值
        data.update(kwargs.pop("data"))
        get_context_data(self.request, self.logger, data=data, **kwargs)
        return context
